﻿using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Dynamic;

namespace Isuka.Excel.Dynamic
{
    /// <summary>
    /// 列をモデルに変換するための動的オブジェクト
    /// </summary>
    public class DynamicRow
        : DynamicObject
    {
        readonly IXGrid grid;

        internal DynamicRow(IXGrid grid, int currentRow = 0)
        {
            Contract.Requires(grid != null);

            this.grid = grid;
            this.CurrentRow = currentRow;
        }

        DynamicRow(DynamicRow other)
            : this(other.grid, other.CurrentRow)
        {
            Contract.Requires<ArgumentNullException>(other != null, "other");
            Contract.Requires(other.grid != null);
        }

        public DynamicRow Copy()
        {
            Contract.Ensures(Contract.Result<DynamicRow>() != null);
            return new DynamicRow(this);
        }

        public int CurrentRow { get; set; }

        #region Overrides DynamicObject

        public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
        {
            return DynamicUtility.AssignValue(binder, out result, _ =>
            {
                Contract.Assume(indexes != null);
                if (indexes.Length == 1 && indexes[0] is int)
                {
                    return grid[CurrentRow, (int)indexes[0]].Value;
                }
                else
                {
                    return false;
                }
            });
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            return DynamicUtility.AssignValue(binder, out result, b =>
            {
                Contract.Assume(!string.IsNullOrEmpty(b.Name));
                return grid[CurrentRow, b.Name].Value;
            });
        }

        public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
        {
            try
            {
                Contract.Assume(indexes != null);
                if (indexes.Length == 1 && indexes[0] is int)
                {
                    grid[CurrentRow, (int)indexes[0]].Value = value;
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception)
            {
                return false;
            }
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            try
            {
                Contract.Assume(!string.IsNullOrEmpty(binder.Name));
                grid[CurrentRow, binder.Name].Value = value;
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        #endregion

        #region ContractInvariantMethod

        [ContractInvariantMethod]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Required for code contracts.")]
        [DebuggerStepThrough]
        private void ObjectInvariant()
        {
            Contract.Invariant(grid != null);
        }

        #endregion
    }
}
